package com.wujunshen.mongodb;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import com.wujunshen.mongodb.properties.MongoGroup;
import com.wujunshen.mongodb.properties.MongoGroups;
import com.wujunshen.mongodb.utils.ConfigurationPropertiesLoader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;

/**
 * User:  FrankWoo(吴峻申) <br>
 * Date:  2018/7/23 <br>
 * Time:  16:38 <br>
 * Email: frank_wjs@hotmail.com <br>
 */
@Slf4j
@Configuration
public class MongoAutoConfiguration implements BeanFactoryPostProcessor, EnvironmentAware {
    private Environment environment;

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        MongoGroups mongoGroups = new MongoGroups();

        ConfigurationPropertiesLoader configurationPropertiesLoader =
                new ConfigurationPropertiesLoader(environment, beanFactory);
        configurationPropertiesLoader.load(mongoGroups);

        for (MongoGroup properties : mongoGroups.getGroups()) {
            MongoClientOptions options = buildMongoOptions(properties);
            List<ServerAddress> seeds = Collections.singletonList(
                    new ServerAddress(properties.getHost(), properties.getPort()));
            MongoClient mongoClient = new MongoClient(seeds, options);
            SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoClient, properties.getDatabase());

            MappingMongoConverter converter = buildConverter(mongoDbFactory, properties.isShowClass());

            //注册mongoTemplate
            registryTemplate(beanFactory, properties.getMongoTemplateName(),
                    new MongoTemplate(mongoDbFactory, converter));

            //如果属性gridFsTemplateName有值,则注册gridFsTemplate
            if (StringUtils.hasText(properties.getGridFsTemplateName())) {
                registryTemplate(beanFactory, properties.getGridFsTemplateName(),
                        new GridFsTemplate(mongoDbFactory, converter));
            }
        }
    }

    /**
     * 注册 mongoTemplate
     */
    private void registryTemplate(ConfigurableListableBeanFactory beanFactory,
                                  String beanName, Object templateBean) {
        beanFactory.registerSingleton(beanName, templateBean);
        beanFactory.applyBeanPostProcessorsAfterInitialization(templateBean, beanName);
        log.info("\n{} has bean loaded\n", beanName);
    }

    private MappingMongoConverter buildConverter(SimpleMongoDbFactory mongoDbFactory,
                                                 boolean showClass) {
        MappingMongoConverter converter = new MappingMongoConverter(
                new DefaultDbRefResolver(mongoDbFactory),
                new MongoMappingContext());
        if (!showClass) {
            converter.setTypeMapper(new DefaultMongoTypeMapper(null));
        }
        return converter;
    }

    private MongoClientOptions buildMongoOptions(MongoGroup properties) {
        if (properties == null) {
            log.info("\n配置文件未装载，请检查配置\n");
            return new MongoClientOptions.Builder().build();
        }

        log.info("\n配置文件已装载\n");

        return new MongoClientOptions.Builder()
                .connectionsPerHost(properties.getMaxConnectionsPerHost())
                .minConnectionsPerHost(properties.getMinConnectionsPerHost())
                .threadsAllowedToBlockForConnectionMultiplier(
                        properties.getThreadsAllowedToBlockForConnectionMultiplier())
                .serverSelectionTimeout(properties.getServerSelectionTimeout())
                .maxWaitTime(properties.getMaxWaitTime())
                .maxConnectionIdleTime(properties.getMaxConnectionIdleTime())
                .maxConnectionLifeTime(properties.getMaxConnectionLifeTime())
                .connectTimeout(properties.getConnectTimeout()).socketTimeout(properties.getSocketTimeout())
                .socketKeepAlive(properties.isSocketKeepAlive()).sslEnabled(properties.isSslEnabled())
                .sslInvalidHostNameAllowed(properties.isSslInvalidHostNameAllowed())
                .alwaysUseMBeans(properties.isAlwaysUseMBeans())
                .heartbeatFrequency(properties.getHeartbeatFrequency())
                .minHeartbeatFrequency(properties.getMinHeartbeatFrequency())
                .heartbeatConnectTimeout(properties.getHeartbeatConnectTimeout())
                .heartbeatSocketTimeout(properties.getSocketTimeout())
                .localThreshold(properties.getLocalThreshold())
                .build();
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}